home *** CD-ROM | disk | FTP | other *** search
Text File | 2010-01-22 | 35.5 KB | 1,071 lines |
- // Gmail Manager
- // By Todd Long <longfocus@gmail.com>
- // http://www.longfocus.com/firefox/gmanager/
-
- const GM_NOTIFY_STATE = "gmanager-accounts-notify-state";
-
- function gmServiceGmail()
- {
- // Load the services
- this._logger = Components.classes["@longfocus.com/gmanager/logger;1"].getService(Components.interfaces.gmILogger);
- this._cookies = Components.classes["@longfocus.com/gmanager/cookies;1"].getService(Components.interfaces.gmICookies);
- this._observer = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
-
- if ("@mozilla.org/xre/app-info;1" in Components.classes)
- {
- var platformVersion = Components.classes["@mozilla.org/xre/app-info;1"].getService(Components.interfaces.nsIXULAppInfo).platformVersion;
- this._swapCookies = (platformVersion === null || platformVersion < "1.8.1");
- }
- }
- gmServiceGmail.prototype = {
- _email: null,
- _password: null,
- _isHosted: false,
- _username: null,
- _domain: null,
- _loginURL: null,
- _checkURL: null,
- _status: Components.interfaces.gmIService.STATE_LOGGED_OUT,
- _loggedIn: false,
- _checking: false,
- _inboxUnread: 0,
- _savedDrafts: 0,
- _spamUnread: 0,
- _spaceUsed: null,
- _percentUsed: null,
- _totalSpace: null,
- _labels: null,
- _snippets: null,
- _timer: null,
- _connectionPhase: 0,
- _channel: null,
- _cookies: null,
- _swapCookies: true,
-
- _log: function(aMsg)
- {
- this._logger.log("(" + this.email + ") " + aMsg);
- },
-
- /**
- * gmIServiceGmail
- */
- get isHosted() { return this._isHosted; },
- get username() { return this._username; },
- get domain() { return this._domain; },
-
- /**
- * gmIService
- */
- get email() { return this._email; },
- get status() { return this._status; },
- get loggedIn() { return this._loggedIn; },
- get checking() { return this._checking; },
- get inboxUnread() { return this._inboxUnread; },
- get savedDrafts() { return this._savedDrafts; },
- get spamUnread() { return this._spamUnread; },
- get spaceUsed() { return this._spaceUsed; },
- get percentUsed() { return this._percentUsed; },
- get totalSpace() { return this._totalSpace; },
-
- getInbox: function(aPassword)
- {
- return this._getService(aPassword);
- },
-
- getCompose: function(aPassword, aHref)
- {
- var href = (aHref ? aHref.replace("mailto:", "&to=").replace("subject=", "su=").replace(/ /g, "%20").replace("?", "&") : "");
- return this._getService(aPassword, "view=cm&fs=1" + href);
- },
-
- _getService: function(aPassword, /* Optional */ aContinueData)
- {
- var serviceURI = new Object();
-
- if (aContinueData == undefined)
- aContinueData = "";
-
- serviceURI.url = this._loginURL;
- serviceURI.data = "ltmpl=default<mplcache=2" +
- "&continue=" + encodeURIComponent(this._checkURL + aContinueData) +
- "&service=mail&rm=false<mpl=default" +
- "&Email=" + encodeURIComponent(this.isHosted ? this.username : this.email) +
- "&Passwd=" + encodeURIComponent(aPassword) +
- "&rmShown=1&signIn=Sign+in";
-
- var xmlHttpRequest = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Components.interfaces.nsIXMLHttpRequest);
-
- // Send the HTTP request
- xmlHttpRequest.open("GET", this._loginURL, false);
- xmlHttpRequest.send(null);
-
- if (xmlHttpRequest.status == 200)
- {
- var httpChannel = xmlHttpRequest.channel.QueryInterface(Components.interfaces.nsIHttpChannel);
- serviceURI.cookies = this._cookieMonster(httpChannel);
- }
-
- if (serviceURI.cookies && "GALX" in serviceURI.cookies)
- serviceURI.data += "&" + serviceURI.cookies["GALX"].pair;
-
- return serviceURI;
- },
-
- getLabels: function(aCount)
- {
- var labels = new Array();
- if (this._labels)
- labels = this._labels;
- aCount.value = labels.length;
- return labels;
- },
-
- getSnippets: function(aCount)
- {
- var snippets = new Array();
- if (this._snippets)
- snippets = this._snippets;
- aCount.value = snippets.length;
- return snippets;
- },
-
- _fixGmailOffline: function()
- {
- // Fix for Gmail Offline (i.e. cookie management)
- var cookieManager = Components.classes["@mozilla.org/cookiemanager;1"].getService(Components.interfaces.nsICookieManager2);
- var cookieEnumerator = cookieManager.enumerator;
- var cookieName = (this.isHosted ? "GAUSR@" + this.domain : "GAUSR");
- var cookieHost = "mail.google.com";
- var gausrExists = false;
-
- while (cookieEnumerator.hasMoreElements() && !gausrExists)
- {
- var cookie = cookieEnumerator.getNext();
-
- if (cookie instanceof Components.interfaces.nsICookie)
- gausrExists = (cookie.host == cookieHost && cookie.name == cookieName);
- }
-
- if (!gausrExists)
- {
- var cookiePath = (this.isHosted ? "/a/" + this.domain : "/mail");
-
- try {
- cookieManager.add(cookieHost, cookiePath, cookieName, this.email, false, false, true, Math.pow(2, 34));
- } catch(e) {
- cookieManager.add(cookieHost, cookiePath, cookieName, this.email, false, true, Math.pow(2, 34));
- }
- }
- },
-
- init: function(aEmail)
- {
- this._email = aEmail.toLowerCase();
- this._isHosted = (this.email.indexOf("@gmail.com") == -1 && this.email.indexOf("@googlemail.com") == -1);
- this._username = this.email.split("@")[0];
- this._domain = this.email.split("@")[1];
-
- // Check if the email is hosted
- if (this.isHosted)
- {
- this._loginURL = "https://www.google.com/a/" + this.domain + "/LoginAction2";
- this._checkURL = "https://mail.google.com/a/" + this.domain + "/?";
- }
- else
- {
- this._loginURL = "https://www.google.com/accounts/ServiceLoginAuth";
- this._checkURL = "https://mail.google.com/mail/?";
- }
- },
-
- login: function(aPassword)
- {
- const emptyRegExp = /^\s*$/;
-
- // Check if already logged in or checking
- if (this.loggedIn || this.checking)
- return;
-
- // Check if the password is specified
- if (aPassword === null || emptyRegExp.test(aPassword))
- {
- this._defaults();
- this._setChecking(false);
- this._setStatus(Components.interfaces.gmIService.STATE_ERROR_PASSWORD);
- }
- else
- {
- // Get the connection details
- var serviceURI = this._getService(aPassword, "labs=0");
-
- // Setup the cookies
- this._cookies = serviceURI.cookies;
-
- // Save the password in case of connection timeout
- this._password = aPassword;
-
- // Fix the cookies for Gmail Offline
- this._fixGmailOffline();
-
- // Set checking and send the server request
- this._setChecking(true);
- this._serverRequest(serviceURI.url, serviceURI.data);
- }
- },
-
- logout: function()
- {
- this._defaults();
- this._setChecking(false);
- this._setStatus(Components.interfaces.gmIService.STATE_LOGGED_OUT);
- },
-
- check: function()
- {
- // Check if already checking
- if (this.checking)
- return;
-
- // Set checking and send the server request
- this._setChecking(true);
- this._serverRequest(this._checkURL + "labs=0");
- },
-
- notify: function(aTimer)
- {
- // Check if already checking
- if (this.checking)
- this._setRetryError(Components.interfaces.gmIService.STATE_ERROR_TIMEOUT);
- else
- {
- // Check if already logged in
- if (this.loggedIn)
- this.check();
- else
- this.login(this._password);
- }
- },
-
- resetUnread: function()
- {
- // Reset the unread counts
- this._inboxUnread = 0;
- this._spamUnread = 0;
- this._snippets = null;
-
- if (this._labels)
- {
- for (var i = 0; i < this._labels.length; i++)
- this._labels[i].unread = 0;
- }
-
- // Update the status so that any observers get notified
- // and can update the account details appropriately
- this._setStatus(this.status);
- },
-
- _setStatus: function(aStatus)
- {
- // Notify the observers with the status
- this._status = aStatus;
- this._observer.notifyObservers(null, GM_NOTIFY_STATE, this.email + "|" + this.status);
- },
-
- _setChecking: function(aChecking)
- {
- if (aChecking)
- {
- // Preserve request cookies (prior to Mozilla 1.8.1)
- if (this._swapCookies)
- {
- // Load the Google cookies
- this._cookies.loadSession("google.com");
- }
-
- try {
- // Add the HTTP observers
- this._observer.addObserver(this, "http-on-modify-request", false);
- this._observer.addObserver(this, "http-on-examine-response", false);
- } catch(e) {}
-
- // Set the status connecting
- this._setStatus(Components.interfaces.gmIService.STATE_CONNECTING);
-
- // Reset the connection phase
- this._connectionPhase = 0;
-
- // Start the timeout timer (30 seconds)
- this._startTimer(30000);
- }
- else
- {
- // Preserve request cookies (prior to Mozilla 1.8.1)
- if (this._swapCookies)
- {
- // Restore the Google cookies
- this._cookies.restoreSession("google.com");
- }
-
- try {
- // Remove the HTTP observers
- this._observer.removeObserver(this, "http-on-modify-request");
- this._observer.removeObserver(this, "http-on-examine-response");
- } catch(e) {}
-
- // Stop the timeout timer
- this._stopTimer();
- }
-
- // Set whether checking or not
- this._checking = aChecking;
- },
-
- _startTimer: function(aInterval)
- {
- var interval = parseInt(aInterval);
-
- // Stop the timeout timer
- this._stopTimer();
-
- // Check if the interval is valid
- if (!isNaN(interval) && interval > 0)
- {
- // Start the timeout timer which fires only once
- this._timer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
- this._timer.initWithCallback(this, interval, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
- }
- },
-
- _stopTimer: function()
- {
- // Check if the timeout timer is active
- if (this._timer)
- {
- // Kill the timeout timer
- this._timer.cancel();
- this._timer = null;
- }
- },
-
- _setRetryError: function(aStatus)
- {
- this._setChecking(false);
- this._setStatus(aStatus);
- this._startTimer(30000);
- },
-
- _defaults: function()
- {
- // Account details
- this._password = null;
- this._loggedIn = false;
- this._checking = false;
- this._inboxUnread = 0;
- this._savedDrafts = 0;
- this._spamUnread = 0;
- this._spaceUsed = null;
- this._percentUsed = null;
- this._totalSpace = null;
- this._labels = null;
- this._snippets = null;
-
- // Login stuff
- this._connectionPhase = 0;
- this._channel = null;
- this._cookies = null;
- },
-
- _serverRequest: function(aURL, /* Optional */ aData)
- {
- this._log("request URL = " + aURL);
-
- var ioService = Components.classes["@mozilla.org/network/io-service;1"].createInstance(Components.interfaces.nsIIOService);
- var uri = ioService.newURI(aURL, null, null);
-
- // Create the HTTP channel to follow
- this._channel = ioService.newChannelFromURI(uri);
-
- // Check for any POST data
- if (typeof aData === "string")
- {
- var stringInputStream = Components.classes["@mozilla.org/io/string-input-stream;1"].createInstance(Components.interfaces.nsIStringInputStream);
- stringInputStream.setData(aData, aData.length);
-
- var uploadChannel = this._channel.QueryInterface(Components.interfaces.nsIUploadChannel);
- uploadChannel.setUploadStream(stringInputStream, "application/x-www-form-urlencoded", -1);
-
- var httpChannel = this._channel.QueryInterface(Components.interfaces.nsIHttpChannel);
- httpChannel.requestMethod = "POST";
- }
-
- // Create the observer for server response
- var observer = new this.observer(this);
-
- // Open the HTTP channel for server request
- this._channel.notificationCallbacks = observer;
- this._channel.asyncOpen(observer, null);
- },
-
- _cookieMonster: function(aHttpChannel, /* Optional */ aCookies)
- {
- var cookies = aCookies;
-
- if (cookies == null)
- cookies = new Object();
-
- if (aHttpChannel != null)
- {
- try {
- var cookieHeader = aHttpChannel.getResponseHeader("Set-Cookie");
-
- String.prototype.firstMatch = function(aRegExp)
- {
- var firstMatch = null;
- try {
- var match = this.match(aRegExp);
- firstMatch = (match && match.length >= 1 ? match[1] : null);
- } catch(e) {
- /* Regular expression format error */
- }
- return firstMatch;
- }
-
- var rawCookies = cookieHeader.split("\n");
-
- for (var i = 0; i < rawCookies.length; i++)
- {
- var cookiePair = rawCookies[i].firstMatch(/^(.*?)(?=;|$)/);
- var cookieName = cookiePair.firstMatch(/(.*?)=/);
- var cookieValue = cookiePair.firstMatch(/=(.*)/);
- var cookieDomain = rawCookies[i].firstMatch(/Domain=(.*?)(?=;|$)/);
- var cookieExpires = rawCookies[i].firstMatch(/Expires=(.*?)(?=;|$)/);
- var cookiePath = rawCookies[i].firstMatch(/Path=(.*?)(?=;|$)/);
-
- if (cookieDomain === null)
- cookieDomain = aHttpChannel.URI.host;
-
- if (cookieExpires !== null)
- cookieExpires = cookieExpires.replace(/-/g, " ");
-
- this._log("cookiePair = " + cookiePair);
-
- // Check if the cookie has expired
- if (Date.parse(cookieExpires) < Date.now())
- delete cookies[cookieName];
- else
- cookies[cookieName] = { pair: cookiePair, name: cookieName, value: cookieValue, domain: cookieDomain, path: cookiePath };
- }
- } catch(e) {}
- }
-
- return cookies;
- },
-
- observe: function(aSubject, aTopic, aData)
- {
- // Check if this is the channel being followed
- if (aSubject == this._channel)
- {
- var httpChannel = aSubject.QueryInterface(Components.interfaces.nsIHttpChannel);
-
- if (this._cookies === null)
- this._cookies = new Object();
-
- switch (aTopic)
- {
- case "http-on-modify-request":
- {
- // Clears the cookies
- httpChannel.setRequestHeader("Cookie", "", false);
-
- for (var cookieName in this._cookies)
- {
- if (httpChannel.URI.host.indexOf(this._cookies[cookieName].domain) > -1)
- httpChannel.setRequestHeader("Cookie", this._cookies[cookieName].pair, true);
- }
-
- break;
- }
- case "http-on-examine-response":
- {
- // Save the incoming cookies
- this._cookies = this._cookieMonster(httpChannel, this._cookies);
-
- // Clear the incoming cookies (Firefox 2.0, Mozilla 1.8.1)
- httpChannel.setResponseHeader("Set-Cookie", "", false);
-
- break;
- }
- }
- }
- },
-
- callback: function(aData, aRequest)
- {
- // Get the http channel containing our data
- var httpChannel = aRequest.QueryInterface(Components.interfaces.nsIHttpChannel);
-
- try {
- const loginRegExp = /\/.*Login.*/i;
-
- // Get HTTP response information
- var status = httpChannel.responseStatus;
- var path = httpChannel.URI.path;
-
- this._log("connection phase = " + this._connectionPhase);
- this._log("http URI path = " + path);
- this._log("http response status = " + status)
- this._log("data = " + aData);
-
- if (status === null || status !== 200) // Bad status
- {
- // Server error, try again in 30 seconds
- this._setRetryError(Components.interfaces.gmIService.STATE_ERROR_NETWORK);
- }
- else if (loginRegExp.test(path) && this._connectionPhase > 0) // Bad password
- {
- this._defaults();
- this._setChecking(false);
- this._setStatus(Components.interfaces.gmIService.STATE_ERROR_PASSWORD);
- }
- } catch(e) {
- // Network error, try again in 30 seconds
- this._setRetryError(Components.interfaces.gmIService.STATE_ERROR_NETWORK);
- }
-
- // Only continue if we're still checking!
- if (this.checking)
- {
- const globalsRegExp = /var\s+GLOBALS\s*=/i;
- const viewDataRegExp = /var\s+VIEW_DATA\s*=/i;
-
- var isLatest = (globalsRegExp.test(aData) && viewDataRegExp.test(aData));
-
- if (isLatest)
- {
- this._log("Using the latest Gmail version =)");
- this._connectionPhase = 2;
- }
- else
- this._log("Using the old Gmail version =(");
-
- // Ok, everything looks good so far =)
- switch (++this._connectionPhase)
- {
- case 1:
- {
- // Send the server request
- this._serverRequest(this._checkURL + "ui=2");
- break;
- }
- case 2:
- {
- // Send the server request
- this._serverRequest(this._checkURL + "ui=1&view=tl&search=inbox&start=0&init=1");
- break;
- }
- case 3:
- {
- // Try to get the account information...
-
- try {
- // Quota
- var quMatches = JSON.fromString(aData.match(/\["qu",(.|\s)+?]/)[0]);
- this._log("\"qu\" match was " + (quMatches ? "found" : "not found"));
-
- this._spaceUsed = quMatches[1];
- this._totalSpace = quMatches[2];
- this._percentUsed = quMatches[3];
-
- this._log("space used = " + this.spaceUsed);
- this._log("total space = " + this.totalSpace);
- this._log("percent used = " + this.percentUsed);
- } catch(e) {
- this._log("error getting the quota: " + e);
- }
-
- try {
- // Initialize the labels
- this._labels = new Array();
-
- // Check which Gmail version
- if (isLatest)
- {
- // Inbox/Drafts/Spam/Labels
- var ldMatches = JSON.fromString(aData.match(/\["ld",(.|\s)+?(\[|])(\s*]){2}/)[0]);
- this._log("\"ld\" match was " + (ldMatches ? "found" : "not found"));
-
- with (Math) {
- this._inboxUnread = max(0, ldMatches[1][0][1]);
- this._savedDrafts = max(0, ldMatches[1][4][1]);
- this._spamUnread = max(0, ldMatches[1][6][1]);
- }
-
- for (var i = 0; i < ldMatches[2].length; i++)
- this._labels.push({
- "name" : ldMatches[2][i][0],
- "unread" : ldMatches[2][i][1],
- "total" : ldMatches[2][i][2]
- });
- }
- else
- {
- // Inbox/Drafts/Spam
- var dsMatches = JSON.fromString(aData.match(/\["ds",(.|\s)+?](\s*]){2}/)[0]);
- this._log("\"ds\" match was " + (dsMatches ? "found" : "not found"));
-
- with (Math) {
- this._inboxUnread = max(0, dsMatches[1][0][1]);
- this._savedDrafts = max(0, dsMatches[1][1][1]);
- this._spamUnread = max(0, dsMatches[1][2][1]);
- }
-
- // Labels
- var ctMatches = JSON.fromString(aData.match(/\["ct",(.|\s)+?](\s*]){2}/)[0]);
- this._log("\"ct\" match was " + (ctMatches ? "found" : "not found"));
-
- for (var i = 0; i < ctMatches[1].length; i++)
- this._labels.push({
- "name" : ctMatches[1][i][0],
- "unread" : ctMatches[1][i][1],
- "total" : -1
- });
- }
-
- this._log("inboxUnread = " + this.inboxUnread);
- this._log("savedDrafts = " + this.savedDrafts);
- this._log("spamUnread = " + this.spamUnread);
-
- if (this._labels.length > 0)
- {
- for (var i = 0; i < this._labels.length; i++)
- {
- var label = this._labels[i];
- var hasTotal = (label.total > -1);
- this._log(label.name + " (" + label.unread + (hasTotal ? " of " + label.total : "") + ")");
- }
- }
- else
- this._log("no labels were found");
- } catch(e) {
- this._log("error getting the inbox/drafts/spam/labels: " + e);
- }
-
- try {
- // Initialize the snippets
- this._snippets = new Array();
-
- // Check which Gmail version
- if (isLatest)
- {
- // Snippets
- var tbMatches = aData.match(/\["tb",(.|\s)+?]\s*]/g);
- this._log("\"tb\" match was " + (tbMatches ? "found" : "not found"));
-
- if (tbMatches === null)
- tbMatches = new Array();
-
- for (var i = 0; i < tbMatches.length; i++)
- {
- var snippets = JSON.fromString(tbMatches[i]);
- var offset = snippets[1];
- var size = snippets[2];
-
- for (var j = 3; j < (size + 3); j++)
- {
- // Check if the snippet is unread
- if (snippets[j][3] == 0)
- {
- this._snippets.push({
- "id" : snippets[j][0],
- "unread" : true,
- "from" : this._replaceHtmlCodes(this._stripHtml(snippets[j][7])),
- "subject" : this._replaceHtmlCodes(this._stripHtml(snippets[j][9])),
- "msg" : this._replaceHtmlCodes(this._stripHtml(snippets[j][10])),
- "time" : this._replaceHtmlCodes(this._stripHtml(snippets[j][14])),
- "date" : snippets[j][15]
- });
- }
- }
- }
- }
- else
- {
- // Snippets
- var tMatches = aData.match(/\["t",(.|\s)+?]\s*]/g);
- this._log("\"t\" match was " + (tMatches ? "found" : "not found"));
-
- if (tMatches === null)
- tMatches = new Array();
-
- for (var i = 0; i < tMatches.length; i++)
- {
- var snippets = JSON.fromString(tMatches[i]);
-
- for (var j = 1; j < snippets.length; j++)
- {
- // Check if the snippet is unread
- if (snippets[j][1] == 1)
- {
- this._snippets.push({
- "id" : snippets[j][0],
- "unread" : true,
- "from" : this._replaceHtmlCodes(this._stripHtml(snippets[j][4])),
- "subject" : this._replaceHtmlCodes(this._stripHtml(snippets[j][6])),
- "msg" : this._replaceHtmlCodes(this._stripHtml(snippets[j][7])),
- "time" : this._replaceHtmlCodes(this._stripHtml(snippets[j][3])),
- "date" : snippets[j][12]
- });
- }
- }
- }
- }
-
- if (this._snippets.length > 0)
- {
- this._log(this._snippets.length + " snippet(s) were found");
-
- for (var i = 0; i < this._snippets.length; i++)
- {
- var snippet = this._snippets[i];
-
- this._log("snippet[" + i + "].id = " + snippet.id);
- this._log("snippet[" + i + "].unread = " + snippet.unread);
- this._log("snippet[" + i + "].from = " + snippet.from);
- this._log("snippet[" + i + "].subject = " + snippet.subject);
- this._log("snippet[" + i + "].msg = " + snippet.msg);
- this._log("snippet[" + i + "].time = " + snippet.time);
- this._log("snippet[" + i + "].date = " + snippet.date);
- }
- }
- else
- this._log("no snippets were found");
- } catch(e) {
- this._log("error getting the snippets: " + e);
- }
-
- this._loggedIn = true;
- this._setChecking(false);
- this._setStatus(Components.interfaces.gmIService.STATE_LOGGED_IN);
-
- break;
- }
- }
- }
- },
-
- _stripHtml: function(aData)
- {
- return aData.replace(/(<([^>]+)>)/ig, "");
- },
-
- _replaceHtmlCodes: function(aData)
- {
- var htmlCodes = new Array(
- [">", ">"], ["<", "<"], ["'", "'"], [""", "\""],
- ["&", "&"], ["˜", "~"], ["™", "?"], ["©", "?"],
- ["®", "?"], ["…", ""] );
-
- for (var i = 0; i < htmlCodes.length; i++)
- {
- var regExp = new RegExp(htmlCodes[i][0], "g");
- aData = aData.replace(regExp, htmlCodes[i][1]);
- }
-
- return aData;
- },
-
- observer: function(aThis)
- {
- return ({
- _data: "",
-
- /**
- * nsIStreamListener
- */
- onStartRequest: function(aRequest, aContext) {
- this._data = "";
- },
-
- onStopRequest: function(aRequest, aContext, aStatus) {
- aThis.callback(this._data, aRequest);
- },
-
- onDataAvailable: function(aRequest, aContext, aStream, aSourceOffset, aLength) {
- var scriptableInputStream = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);
- scriptableInputStream.init(aStream);
- this._data += scriptableInputStream.read(aLength);
- },
-
- /**
- * nsIChannelEventSink
- */
- onChannelRedirect: function (aOldChannel, aNewChannel, aFlags) {
- aThis._channel = aNewChannel;
- },
-
- /**
- * nsIProgressEventSink
- */
- onProgress: function (aRequest, aContext, aProgress, aProgressMax) { /* Stub */ },
- onStatus: function (aRequest, aContext, aStatus, aStatusArg) { /* Stub */ },
-
- /**
- * nsIHttpEventSink
- */
- onRedirect: function (aOldChannel, aNewChannel) { /* Stub */ },
-
- /**
- * nsIInterfaceRequestor
- */
- getInterface: function(aIID) {
- try {
- return this.QueryInterface(aIID);
- } catch(e) {
- throw Components.results.NS_NOINTERFACE;
- }
- },
-
- QueryInterface: function(aIID) {
- if (aIID.equals(Components.interfaces.nsIStreamListener) ||
- aIID.equals(Components.interfaces.nsIChannelEventSink) ||
- aIID.equals(Components.interfaces.nsIProgressEventSink) ||
- aIID.equals(Components.interfaces.nsIHttpEventSink) ||
- aIID.equals(Components.interfaces.nsIInterfaceRequestor) ||
- aIID.equals(Components.interfaces.nsISupports))
- return this;
- throw Components.results.NS_ERROR_NO_INTERFACE;
- }
- });
- },
-
- QueryInterface: function(iid)
- {
- if (iid.equals(Components.interfaces.gmIServiceGmail) ||
- iid.equals(Components.interfaces.nsISupports))
- return this;
- throw Components.results.NS_ERROR_NO_INTERFACE;
- }
- }
-
- var myModule = {
- firstTime: true,
-
- myCID: Components.ID("{b07df9d0-f7dd-11da-974d-0800200c9a66}"),
- myDesc: "Gmail Account Service",
- myProgID: "@longfocus.com/gmanager/service/gmail;1",
- myFactory: {
- createInstance: function (outer, iid) {
- if (outer != null)
- throw Components.results.NS_ERROR_NO_AGGREGATION;
-
- return (new gmServiceGmail()).QueryInterface(iid);
- }
- },
-
- registerSelf: function (compMgr, fileSpec, location, type)
- {
- if (this.firstTime) {
- this.firstTime = false;
- throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
- }
-
- compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
- compMgr.registerFactoryLocation(this.myCID, this.myDesc, this.myProgID, fileSpec, location, type);
- },
-
- getClassObject: function (compMgr, cid, iid)
- {
- if (!cid.equals(this.myCID))
- throw Components.results.NS_ERROR_NO_INTERFACE;
-
- if (!iid.equals(Components.interfaces.nsIFactory))
- throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
-
- return this.myFactory;
- },
-
- canUnload: function(compMgr) { return true; }
- };
-
- function NSGetModule(compMgr, fileSpec) { return myModule; }
-
- /* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla code.
- *
- * The Initial Developer of the Original Code is
- * Simon B├╝nzli <zeniko@gmail.com>
- * Portions created by the Initial Developer are Copyright (C) 2006-2007
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
- /**
- * Utilities for JavaScript code to handle JSON content.
- * See http://www.json.org/ for comprehensive information about JSON.
- *
- * Import this module through
- *
- * Components.utils.import("resource://gre/modules/JSON.jsm");
- *
- * Usage:
- *
- * var newJSONString = JSON.toString( GIVEN_JAVASCRIPT_OBJECT );
- * var newJavaScriptObject = JSON.fromString( GIVEN_JSON_STRING );
- *
- * Note: For your own safety, Objects/Arrays returned by
- * JSON.fromString aren't instanceof Object/Array.
- */
-
- var EXPORTED_SYMBOLS = ["JSON"];
-
- // The following code is a loose adaption of Douglas Crockford's code
- // from http://www.json.org/json.js (public domain'd)
-
- // Notable differences:
- // * Unserializable values such as |undefined| or functions aren't
- // silently dropped but always lead to a TypeError.
- // * An optional key blacklist has been added to JSON.toString
-
- var JSON = {
- /**
- * Converts a JavaScript object into a JSON string.
- *
- * @param aJSObject is the object to be converted
- * @param aKeysToDrop is an optional array of keys which will be
- * ignored in all objects during the serialization
- * @return the object's JSON representation
- *
- * Note: aJSObject MUST not contain cyclic references.
- */
- toString: function JSON_toString(aJSObject, aKeysToDrop) {
- // we use a single string builder for efficiency reasons
- var pieces = [];
-
- // this recursive function walks through all objects and appends their
- // JSON representation (in one or several pieces) to the string builder
- function append_piece(aObj) {
- if (typeof aObj == "string") {
- aObj = aObj.replace(/[\\"\x00-\x1F\u0080-\uFFFF]/g, function($0) {
- // use the special escape notation if one exists, otherwise
- // produce a general unicode escape sequence
- switch ($0) {
- case "\b": return "\\b";
- case "\t": return "\\t";
- case "\n": return "\\n";
- case "\f": return "\\f";
- case "\r": return "\\r";
- case '"': return '\\"';
- case "\\": return "\\\\";
- }
- return "\\u" + ("0000" + $0.charCodeAt(0).toString(16)).slice(-4);
- });
- pieces.push('"' + aObj + '"')
- }
- else if (typeof aObj == "boolean") {
- pieces.push(aObj ? "true" : "false");
- }
- else if (typeof aObj == "number" && isFinite(aObj)) {
- // there is no representation for infinite numbers or for NaN!
- pieces.push(aObj.toString());
- }
- else if (aObj === null) {
- pieces.push("null");
- }
- // if it looks like an array, treat it as such - this is required
- // for all arrays from either outside this module or a sandbox
- else if (aObj instanceof Array ||
- typeof aObj == "object" && "length" in aObj &&
- (aObj.length === 0 || aObj[aObj.length - 1] !== undefined)) {
- pieces.push("[");
- for (var i = 0; i < aObj.length; i++) {
- arguments.callee(aObj[i]);
- pieces.push(",");
- }
- if (aObj.length > 0)
- pieces.pop(); // drop the trailing colon
- pieces.push("]");
- }
- else if (typeof aObj == "object") {
- pieces.push("{");
- for (var key in aObj) {
- // allow callers to pass objects containing private data which
- // they don't want the JSON string to contain (so they don't
- // have to manually pre-process the object)
- if (aKeysToDrop && aKeysToDrop.indexOf(key) != -1)
- continue;
-
- arguments.callee(key.toString());
- pieces.push(":");
- arguments.callee(aObj[key]);
- pieces.push(",");
- }
- if (pieces[pieces.length - 1] == ",")
- pieces.pop(); // drop the trailing colon
- pieces.push("}");
- }
- else {
- throw new TypeError("No JSON representation for this object!");
- }
- }
- append_piece(aJSObject);
-
- return pieces.join("");
- },
-
- /**
- * Converts a JSON string into a JavaScript object.
- *
- * @param aJSONString is the string to be converted
- * @return a JavaScript object for the given JSON representation
- */
- fromString: function JSON_fromString(aJSONString) {
- if (!this.isMostlyHarmless(aJSONString))
- throw new SyntaxError("No valid JSON string!");
-
- var s = new Components.utils.Sandbox("about:blank");
- return Components.utils.evalInSandbox("(" + aJSONString + ")", s);
- },
-
- /**
- * Checks whether the given string contains potentially harmful
- * content which might be executed during its evaluation
- * (no parser, thus not 100% safe! Best to use a Sandbox for evaluation)
- *
- * @param aString is the string to be tested
- * @return a boolean
- */
- isMostlyHarmless: function JSON_isMostlyHarmless(aString) {
- const maybeHarmful = /[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/;
- const jsonStrings = /"(\\.|[^"\\\n\r])*"/g;
-
- return !maybeHarmful.test(aString.replace(jsonStrings, ""));
- }
- };